/* Copyright (C) 2008  Romolo Camplani, Marco Fortunato, Marco Marelli, Guido Rota, Fabio A. Schreiber et al.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.dei.perla.parser.expressions;

/**
 * Costante di tipo LOGIC
 */
public class ConstantLogic extends ConstantBuiltin {
	
	// Valore della costante
	private LogicValue logicValue;
	
	
	/**
	 *  Crea una costante impostata al valore specificato
	 */
	public ConstantLogic(LogicValue parValue) {
		this.initialize(parValue);
	}
	
	public ConstantLogic(boolean parValue) {
		if(parValue)
			this.initialize(LogicValue.TRUE);
		else
			this.initialize(LogicValue.FALSE);
	}
	
	
	/**
	 * Crea una costante impostata al valore di default
	 */
	public ConstantLogic() {
		this.initialize(LogicValue.FALSE);
	}
	
	
	/**
	 * Crea una costante a partire dalla stringa passata 
	 */
	public ConstantLogic(String parStringValue) throws ConstantCreationException {
		
		LogicValue tLogicValue = null;
		
		
		// Effettua la conversione da stringa a LogicValue
		if(parStringValue != null){			
			if("TRUE".equals(parStringValue.toUpperCase()))
				tLogicValue = LogicValue.TRUE;
			else if("FALSE".equals(parStringValue.toUpperCase()))
				tLogicValue = LogicValue.FALSE;
			else if("UNKNOWN".equals(parStringValue.toUpperCase()))
				tLogicValue = LogicValue.UNKNOWN;
			else
				throw new ConstantCreationException();
		}
		
		// Se la conversione non � andata a buon fine, solleva un'eccezione
		if(tLogicValue == null)
			throw new ConstantCreationException();
		
		// Inizializza la costante con il LogicValue determinato
		this.initialize(tLogicValue);		
	}
	
	
	/**
	 * Inizializza la costante	
	 */
	private void initialize(LogicValue parValue){
		this.logicValue = parValue;
	}
	
	
	/**
	 * Casta il valore della costante a Logic
	 */
	public LogicValue getValueLogic() {
		return this.logicValue;
	}
	
	/**
	 * Restituisce un oggetto ConstantLogic (o ConstantNull in alcuni casi) rispecchiante l'operazione AND
	 * tra la classe e l'oggetto parConst passaro come parametro.
	 */
	public ConstantLogic and(Constant parConst) throws ConstantCastException, OperationNotSupportedException {
		if (parConst.getClass() == ConstantLogic.class) {
			if ((logicValue == LogicValue.TRUE) && (logicValue == parConst.getValueLogic()))
				return new ConstantLogic(LogicValue.TRUE);
			else if ((logicValue == LogicValue.UNKNOWN) && (logicValue == parConst.getValueLogic()))
				return new ConstantLogic(LogicValue.UNKNOWN);
			else if ((logicValue == LogicValue.TRUE) && (parConst.getValueLogic() == LogicValue.UNKNOWN))
				return new ConstantLogic(LogicValue.UNKNOWN);
			else if ((logicValue == LogicValue.UNKNOWN) && (parConst.getValueLogic() == LogicValue.TRUE))
				return new ConstantLogic(LogicValue.UNKNOWN);
			else if ((logicValue == LogicValue.FALSE) || (parConst.getValueLogic() == LogicValue.FALSE))
				return new ConstantLogic(LogicValue.FALSE);
			else
				throw new ConstantCastException("parConst is not a known logical value!");
		}
		else if (parConst.getClass() == ConstantNull.class)
			return new ConstantLogic(LogicValue.UNKNOWN);
		else if (parConst.isUserDefined())
			return parConst.andInverse(this);
		else
			return super.and(parConst);

	}
	
	public static Class<? extends Constant> andResultType(Class<? extends Constant> parConst) throws OperationNotSupportedException {
		//if (parConst== ConstantLogic.class)
		if (parConst == ConstantLogic.class || parConst == ConstantNull.class)
			return ConstantLogic.class;
		else if (parConst.getSuperclass() == ConstantUserDefined.class) {
			try {
				return ((Class<? extends Constant>) parConst.getMethod("andInverseResultType", Class.class).invoke(null, ConstantLogic.class));
			} catch (Exception e) {
				throw new OperationNotSupportedException("AND Operation not supported with " + parConst.toString());
			}
		}
		else
			throw new OperationNotSupportedException("AND Operation not supported with " + parConst.toString());
	}
	
	/**
	 * Restituisce un oggetto ConstantLogic (o ConstantNull in alcuni casi) rispecchiante l'operazione OR
	 * tra la classe e l'oggetto parConst passaro come parametro.
	 */
	public ConstantLogic or(Constant parConst) throws ConstantCastException, OperationNotSupportedException {
		if (parConst.getClass() == ConstantLogic.class) {
			if ((logicValue == LogicValue.TRUE) || (parConst.getValueLogic() == LogicValue.TRUE))
				return new ConstantLogic(LogicValue.TRUE);
			else if ((logicValue == LogicValue.UNKNOWN) || (parConst.getValueLogic() == LogicValue.UNKNOWN))
				return new ConstantLogic(LogicValue.UNKNOWN);
			else if ((logicValue == LogicValue.FALSE) && (parConst.getValueLogic() == LogicValue.FALSE)) 
				return new ConstantLogic(LogicValue.FALSE);
			else
				throw new ConstantCastException("parConst is not a known logical value!");
		}
		else if (parConst.getClass() == ConstantNull.class)
			return new ConstantLogic(LogicValue.UNKNOWN);
		else if (parConst.isUserDefined())
			return parConst.orInverse(this);
		else
			return super.or(parConst);

	}
	
	public static Class<? extends Constant> orResultType(Class<? extends Constant> parConst) throws OperationNotSupportedException {
		if (parConst== ConstantLogic.class || parConst == ConstantNull.class)
			return ConstantLogic.class;
		else if (parConst.getSuperclass() == ConstantUserDefined.class) {
			try {
				return ((Class<? extends Constant>) parConst.getMethod("orInverseResultType", Class.class).invoke(null, ConstantLogic.class));
			} catch (Exception e) {
				throw new OperationNotSupportedException("OR Operation not supported with " + parConst.toString());
			}
		}
		else
			throw new OperationNotSupportedException("OR Operation not supported with " + parConst.toString());
	}
	
	/**
	 * Restituisce un oggetto ConstantLogic (o ConstantNull in alcuni casi) rispecchiante l'operazione XOR
	 * tra la classe e l'oggetto parConst passaro come parametro.
	 */
	public ConstantLogic xor(Constant parConst) throws ConstantCastException, OperationNotSupportedException {
		if (parConst.getClass() == ConstantLogic.class) {
			if ((logicValue == LogicValue.TRUE) && (parConst.getValueLogic() == LogicValue.TRUE))
				return new ConstantLogic(LogicValue.FALSE);
			else if ((logicValue == LogicValue.UNKNOWN) || (parConst.getValueLogic() == LogicValue.UNKNOWN))
				return new ConstantLogic(LogicValue.UNKNOWN);
			else if ((logicValue == LogicValue.TRUE) || (parConst.getValueLogic() == LogicValue.TRUE))
				return new ConstantLogic(LogicValue.TRUE);
			else if ((logicValue == LogicValue.FALSE) && (parConst.getValueLogic() == LogicValue.FALSE)) 
				return new ConstantLogic(LogicValue.FALSE);
			else
				throw new ConstantCastException("parConst is not a known logical value!");
		}
		else if (parConst.getClass() == ConstantNull.class)
			return new ConstantLogic(LogicValue.UNKNOWN);
		else if (parConst.isUserDefined())
			return parConst.xorInverse(this);
		else
			return super.xor(parConst);

	}
	
	public static Class<? extends Constant> xorResultType(Class<? extends Constant> parConst) throws OperationNotSupportedException {
		if (parConst== ConstantLogic.class || parConst == ConstantNull.class)
			return ConstantLogic.class;
		else if (parConst.getSuperclass() == ConstantUserDefined.class) {
			try {
				return ((Class<? extends Constant>) parConst.getMethod("xorInverseResultType", Class.class).invoke(null, ConstantLogic.class));
			} catch (Exception e) {
				throw new OperationNotSupportedException("XOR Operation not supported with " + parConst.toString());
			}
		}
		else
			throw new OperationNotSupportedException("XOR Operation not supported with " + parConst.toString());
	}
	
	/**
	 * Restituisce un oggetto ConstantLogic (o ConstantNull in alcuni casi) rispecchiante l'operazione NOT
	 * sulla classe stessa.
	 */
	public ConstantLogic not() throws OperationNotSupportedException {
		if (logicValue == LogicValue.TRUE)
			return new ConstantLogic(LogicValue.FALSE);
		else if (logicValue == LogicValue.FALSE)
			return new ConstantLogic(LogicValue.TRUE);
		else
			return new ConstantLogic(LogicValue.UNKNOWN);
	}
	
	public static Class<? extends Constant> notResultType() throws OperationNotSupportedException {
		return ConstantLogic.class;
	}
	
	public ConstantLogic is(Constant parConst) throws ConstantCastException, OperationNotSupportedException {
		if (parConst.getClass() == ConstantLogic.class) {
			if (this.logicValue == parConst.getValueLogic())
				return new ConstantLogic(LogicValue.TRUE);
			else
				return new ConstantLogic(LogicValue.FALSE);
		}
		else
			return super.is(parConst);
	}
	
	public static Class<? extends Constant> isResultType(Class<? extends Constant> parConst) throws OperationNotSupportedException {
		if (parConst== ConstantLogic.class || parConst == ConstantNull.class)
			return ConstantLogic.class;
		else if (parConst.getSuperclass() == ConstantUserDefined.class) {
			try {
				return ((Class<? extends Constant>) parConst.getMethod("isInverseResultType", Class.class).invoke(null, ConstantLogic.class));
			} catch (Exception e) {
				throw new OperationNotSupportedException("IS Operation not supported with " + parConst.toString());
			}
		}
		else
			throw new OperationNotSupportedException("IS Operation not supported with " + parConst.toString());
	}
	
	public ConstantLogic isEqual(Constant parConst) throws ConstantCastException, OperationNotSupportedException {
		if (parConst.getClass() == ConstantLogic.class) {
		if (this.logicValue==LogicValue.UNKNOWN || parConst.getValueLogic()==LogicValue.UNKNOWN) {
			return new ConstantLogic(LogicValue.UNKNOWN);
		}
		else if (this.logicValue == parConst.getValueLogic())
				return new ConstantLogic(LogicValue.TRUE);
			else
				return new ConstantLogic(LogicValue.FALSE);
		}
		else if (parConst.getClass() == ConstantNull.class)
			return new ConstantLogic(LogicValue.UNKNOWN);
		else
			return super.isEqual(parConst);
	}
	
	public ConstantLogic isUnequal(Constant parConst) throws ConstantCastException, OperationNotSupportedException {
		if (parConst.getClass() == ConstantLogic.class) {
			if (this.logicValue==LogicValue.UNKNOWN || parConst.getValueLogic()==LogicValue.UNKNOWN) {
				return new ConstantLogic(LogicValue.UNKNOWN);
			}
			if (this.logicValue != parConst.getValueLogic())
				return new ConstantLogic(LogicValue.TRUE);
			else
				return new ConstantLogic(LogicValue.FALSE);
		}
		else if (parConst.getClass() == ConstantNull.class)
			return new ConstantLogic(LogicValue.UNKNOWN);
		else
			return super.isUnequal(parConst);
	}
	
	/**
	 * Dato un tipo Constant il metodo ritorna true se ConstantLogic supporta le operazioni
	 * di uguaglianza e disuguaglianza con la classe Constant passata come parametro. In caso contrario ritorna false.
	 * @param parConst
	 * @return
	 */
	public static boolean isEquallyComparable(Class<? extends Constant> parConst) {
		if (parConst== ConstantLogic.class)
			return true;
		else if (parConst.getSuperclass() == ConstantUserDefined.class) {
			try {
				return ((Boolean) parConst.getMethod("isEquallyComparable", Class.class).invoke(null, ConstantLogic.class)).booleanValue();
			} catch (Exception e) {
				return false;
			}
		}
		else
			return false;
	}
	
	/**
	 * Restituisce una stringa rappresentante il valore della Costante
	 */
	public String toString() {
		return String.valueOf(logicValue);
	}
}